{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Checkpoints Design Pattern\n",
    "\n",
    "This notebook demonstrates how to set up checkpointing in Keras.\n",
    "\n",
    "The model tries to predict whether or not a ride includes a toll."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating dataset\n",
    "\n",
    "Create dataset from BigQuery. The dataset consists of 19 millions rows and will not comfortably fit into memory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow.python.framework import dtypes\n",
    "from tensorflow_io.bigquery import BigQueryClient\n",
    "from tensorflow_io.bigquery import BigQueryReadSession\n",
    "\n",
    "def features_and_labels(features):\n",
    "  label = features.pop('tolls_amount') # this is what we will train for\n",
    "  return features, tf.cast(label > 0, dtypes.int64, name='threshold')\n",
    "\n",
    "def read_dataset(client, row_restriction, batch_size=2048, infinite=True):\n",
    "    GCP_PROJECT_ID='ai-analytics-solutions'  # CHANGE\n",
    "    COL_NAMES = ['pickup_latitude', 'pickup_longitude', 'dropoff_latitude', 'dropoff_longitude', 'tolls_amount']\n",
    "    COL_TYPES = [dtypes.float64] * len(COL_NAMES)\n",
    "    DATASET_GCP_PROJECT_ID, DATASET_ID, TABLE_ID,  = 'bigquery-public-data.new_york.tlc_green_trips_2015'.split('.')\n",
    "    bqsession = client.read_session(\n",
    "        \"projects/\" + GCP_PROJECT_ID,\n",
    "        DATASET_GCP_PROJECT_ID, TABLE_ID, DATASET_ID,\n",
    "        COL_NAMES, COL_TYPES,\n",
    "        requested_streams=2,\n",
    "        row_restriction=row_restriction + ' AND pickup_longitude > -80 AND dropoff_longitude < -70')\n",
    "    dataset = bqsession.parallel_read_rows()\n",
    "    dataset = dataset.prefetch(1).map(features_and_labels).shuffle(batch_size*10).batch(batch_size)\n",
    "    if infinite:\n",
    "        dataset = dataset.repeat()\n",
    "    return dataset\n",
    "\n",
    "client = BigQueryClient()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/tensorflow_io/bigquery/python/ops/bigquery_api.py:214: parallel_interleave (from tensorflow.python.data.experimental.ops.interleave_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use `tf.data.Dataset.interleave(map_func, cycle_length, block_length, num_parallel_calls=tf.data.experimental.AUTOTUNE)` instead. If sloppy execution is desired, use `tf.data.Options.experimental_deterministic`.\n",
      "(OrderedDict([('dropoff_latitude', <tf.Tensor: shape=(2,), dtype=float64, numpy=array([40.80184555, 40.79564667])>), ('dropoff_longitude', <tf.Tensor: shape=(2,), dtype=float64, numpy=array([-73.94499207, -73.94239807])>), ('pickup_latitude', <tf.Tensor: shape=(2,), dtype=float64, numpy=array([40.80989838, 40.81524658])>), ('pickup_longitude', <tf.Tensor: shape=(2,), dtype=float64, numpy=array([-73.95143127, -73.92116547])>)]), <tf.Tensor: shape=(2,), dtype=int64, numpy=array([0, 0])>)\n"
     ]
    }
   ],
   "source": [
    "temp_df = read_dataset(client, \"pickup_datetime BETWEEN '2015-01-01' AND '2015-03-31'\", 2)\n",
    "for row in temp_df:\n",
    "    print(row)\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "BATCH_SIZE=2048\n",
    "train_df = read_dataset(client, \"pickup_datetime BETWEEN '2015-01-01' AND '2015-03-31'\", BATCH_SIZE)\n",
    "eval_df = read_dataset(client, \"pickup_datetime BETWEEN '2015-04-01' AND '2015-04-30'\", BATCH_SIZE, infinite=False) # for validation, read it only once"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Layer features is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2.  The layer has dtype float32 because it's dtype defaults to floatx.\n",
      "\n",
      "If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.\n",
      "\n",
      "To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABDsAAAEYCAYAAABSs/0dAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdeXxN1/4//tfJSSKTEGkJScgoiF6KprTCRfhWr6mtJB2Ij6GRVilRlaKDmm+bmuKiVGlMjeFSt9VS1E00SnUQQtAQiQgSkTkyvX9/+GVfkemcTCfn5PV8PM7jIevsvfZ7r7XO3ue87b22SkQERERERERERESGYZeRriMgIiIiIiIiIqpLTHYQERERERERkUFhsoOIiIiIiIiIDAqTHURERERERERkUIy1WXjXrl3YtWtXfcVCRDXg4OCAzz77TNdhEBERERERNRpaXdmxa9cuREdH11csRKSlxMRELF++XNdhEBERERERNSpaXdkBAH369EFERER9xEJEWoqIiIC/v7+uwyAiIiIiImpUOGcHERERERERERkUJjuIiIiIiIiIyKAw2UFEREREREREBoXJDiIiIiIiIiIyKEx2EBEREREREZFBYbKDiIiIiIiIiAwKkx1EREREREREZFCY7CAiIiIiIiIig8JkBxEREREREREZFCY7iIiIiIiIiMigMNlBRERERERERAaFyQ4iIiIiIiIiMihMdhARERERERGRQWGyg4iIiIiIiIgMis6SHffu3dPVphvMrVu3EBERgUWLFuk6lGo1hf4gIiIiIiKipqFBkx35+flYtGgR+vTpA1tb24bctEZEBKtWrcLs2bMxYMAAeHt7Iy4urtLyqly4cAEff/wx/P39ER4eXuvYnn76acyaNavW9TysPvsjMjISISEhUKlUUKlUCAgIwP79++t0GzVx7Ngx+Pr6KnFNnjwZJ06c0HVYREREREREVIdUIiKaLuzn5wcAiIiIqPEG8/LyYG9vj/T0dGix6QaxcuVKzJ07F/fu3UN2djYmTJiAkJAQREdHV1ju5eVVZX35+fkwNzeHh4cHLl68qHEciYmJcHR0LFP28ssvw93dHQsWLKhyOW3Vd3906NAB169fR05ODiwsLOq8fk082k65ubmwtLRE+/btkZCQoJOY6kpERAT8/f0b3WeJiIiIiIhIh3YZN/QWzc3N0bp1a6Snpzf0pqu1du1a2Nvbw9jYGC1btsTevXsBAAEBARWWV8fMzEzrGK5evYqAgABERkaWKd+5c6dGy2mrvvvD3NwcAHSW6KionUpj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K1euKK9du3bh+vXrSlKjdevWcHNzg5ubG/773/8qCZKHFRYWorCwEKGhofjXv/6F4OBgBAcHa3zFCBERERERUUWY7CAirTRr1gydOnVCp06dyr1XUFCAa9euKQmQ0oRISkpKlXUWFRUhKysLixYtwsqVKxESEoLi4mKo1er62g0iIiIiIjJglSY7DOEZ2URNWWJiIhwdHRt0m6ampujYsSM6duyolGVkZGh8e0pRURHu3buHkJAQmJmZ4amnnqqvUImIiIiIyICVS3a0bdsWxsbG8PPz00U8RFSH+vTpo+sQcOXKFY2WMzIygrGxMQoKCgA8eJpMXl5efYZGREREREQGqlyyw9vbW5k8kIiotq5cuQKVSgURUcpUKhXUajWKioqgUqlgb2+P3r1746mnnkLPnj3Ro0cPTJ48WYdRExERERGRPuOcHURUr+Lj45VEh0qlgqOjI/r06YNevXqhR48e6NGjB5/CQkREREREdYrJDiKqV6NGjYKFhQW6deuGJ598sswjaYmIiIiIiOoDkx1EVK86d+6Mzp076zoMIiIiIiJqQox0HQARERERERERUV1isoOIiIgavXv37uk6BNJTHDukKxx7VBMcN3WHyQ4iMigiglWrVmH27NkYMGAAvL29ERcXp+uwGtThw4cxdOhQqFQqqFQqDBgwAAMGDECvXr0wYsQIbNy4Effv39d1mGV88sknaNmypfKkniFDhmDYsGH4xz/+gUGDBqF9+/ZQqVS4fv26rkOlBpSfn49FixahT58+sLW1LfPejRs3sGnTJvj5+dX4Mdv6+FkhzVQ2dkQEGzduRPfu3WFlZYVu3bph06ZNZZ4YpgmOHapMVcet8+fPY+TIkbC1tcVjjz2Gl19+GcnJyVrVz7FnmKoaNw9btWoVVCqV1vU32XEjRESNkK+vr/j6+mq93ooVK8TS0lIKCwslPT1dXnjhBfnll19qHc/169drXUdDSkpKEgDi5OSklBUXF8s333wjLi4u4ubmJufOndNhhOXduHFDAIibm1u594qLi+Uf//iHXLlypU62pU/9CUC+/vprXYchEREREhISIn/++adW69U2/tzcXLGxsZGKvrIkJCQIAPHw8Khx/fr4WdHE119/XWGb6cKcOXNk1apVkpKSovE6dRF/RWNn9uzZ8tprr0lYWJhMmzZNzMzMBICsWrVK6/oNdeyU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\n",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics = [\n",
    "      tf.keras.metrics.BinaryAccuracy(name='accuracy'),\n",
    "      tf.keras.metrics.Precision(name='precision'),\n",
    "      tf.keras.metrics.Recall(name='recall'),\n",
    "      tf.keras.metrics.AUC(name='roc_auc'),\n",
    "]\n",
    "\n",
    "# create inputs, and pass them into appropriate types of feature columns (here, everything is numeric)\n",
    "inputs = {\n",
    "    colname : tf.keras.layers.Input(name=colname, shape=(), dtype='float64')\n",
    "    for colname in ['pickup_latitude', 'pickup_longitude', 'dropoff_latitude', 'dropoff_longitude']\n",
    "}\n",
    "input_fc = [tf.feature_column.numeric_column(colname) for colname in inputs.keys()]\n",
    "\n",
    "# transformations, pass through\n",
    "transformed = inputs.copy()\n",
    "input_layer = tf.keras.layers.DenseFeatures(input_fc, name='features')(transformed)\n",
    "\n",
    "# Deep learning model\n",
    "d1 = tf.keras.layers.Dense(16, activation='relu', name='d1')(input_layer)\n",
    "d2 = tf.keras.layers.Dropout(0.25, name='d2')(d1)\n",
    "d3 = tf.keras.layers.Dense(16, activation='relu', name='d3')(d2)\n",
    "output = tf.keras.layers.Dense(1, activation='sigmoid', name='d4', bias_initializer=tf.keras.initializers.Constant())(d3)\n",
    "\n",
    "model = tf.keras.Model(inputs, output)\n",
    "model.compile(optimizer='adam',\n",
    "              loss='binary_crossentropy',\n",
    "              metrics=metrics)\n",
    "tf.keras.utils.plot_model(model, rankdir='LR')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train for 854 steps\n",
      "Epoch 1/10\n",
      "854/854 [==============================] - 374s 438ms/step - loss: 0.6196 - accuracy: 0.9517 - precision: 0.0377 - recall: 0.0769 - roc_auc: 0.4323 - val_loss: 0.6709 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 2/10\n",
      "854/854 [==============================] - 377s 442ms/step - loss: 0.6333 - accuracy: 0.9831 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3662 - val_loss: 0.6721 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 3/10\n",
      "854/854 [==============================] - 376s 440ms/step - loss: 0.6643 - accuracy: 0.9814 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3757 - val_loss: 0.6713 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 4/10\n",
      "854/854 [==============================] - 369s 433ms/step - loss: 0.6169 - accuracy: 0.8735 - precision: 0.0265 - recall: 0.1828 - roc_auc: 0.5230 - val_loss: 0.6712 - val_accuracy: 0.9798 - val_precision: 0.0065 - val_recall: 3.9066e-04 - val_roc_auc: 0.4996\n",
      "Epoch 5/10\n",
      "854/854 [==============================] - 372s 436ms/step - loss: 0.6450 - accuracy: 0.9825 - precision: 0.0147 - recall: 3.2674e-05 - roc_auc: 0.3684 - val_loss: 0.6722 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 6/10\n",
      "854/854 [==============================] - 376s 441ms/step - loss: 0.6545 - accuracy: 0.9820 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3817 - val_loss: 0.6714 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 7/10\n",
      "854/854 [==============================] - 374s 438ms/step - loss: 0.6300 - accuracy: 0.9833 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3566 - val_loss: 0.6720 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 8/10\n",
      "854/854 [==============================] - 368s 431ms/step - loss: 0.6607 - accuracy: 0.9816 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3659 - val_loss: 0.6718 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 9/10\n",
      "854/854 [==============================] - 376s 440ms/step - loss: 0.6382 - accuracy: 0.9828 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3848 - val_loss: 0.6716 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n",
      "Epoch 10/10\n",
      "854/854 [==============================] - 378s 443ms/step - loss: 0.6310 - accuracy: 0.9832 - precision: 0.0000e+00 - recall: 0.0000e+00 - roc_auc: 0.3622 - val_loss: 0.6721 - val_accuracy: 0.9809 - val_precision: 0.0000e+00 - val_recall: 0.0000e+00 - val_roc_auc: 0.5004\n"
     ]
    }
   ],
   "source": [
    "class_weight = {0: 0.5, 1: 25.0}\n",
    "\n",
    "OUTDIR='trained'\n",
    "import shutil\n",
    "shutil.rmtree(OUTDIR, ignore_errors=True)\n",
    "\n",
    "NUM_TRAINING_EXAMPLES = 1000 * 1000 * 5\n",
    "STOP_POINT = 3.5\n",
    "TOTAL_TRAINING_EXAMPLES = int(STOP_POINT * NUM_TRAINING_EXAMPLES)\n",
    "NUM_CHECKPOINTS = 10\n",
    "steps_per_epoch = (TOTAL_TRAINING_EXAMPLES // \n",
    "                   (BATCH_SIZE*NUM_CHECKPOINTS))\n",
    "\n",
    "checkpoint_path = '{}/checkpoints/taxi'.format(OUTDIR)\n",
    "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, \n",
    "                                                 save_weights_only=False,\n",
    "                                                 verbose=1)\n",
    "\n",
    "history = model.fit(train_df, validation_data=eval_df, \n",
    "                    epochs=NUM_CHECKPOINTS,\n",
    "                    steps_per_epoch=steps_per_epoch,\n",
    "                    class_weight=class_weight)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2020 Google Inc. Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
